i.MX 6ULL 驱动开发 二十七:块设备

您所在的位置:网站首页 unity cacheline i.MX 6ULL 驱动开发 二十七:块设备

i.MX 6ULL 驱动开发 二十七:块设备

2023-03-19 16:25| 来源: 网络整理| 查看: 265

参考:【块设备】通用块层 struct bio 详解 | zzm (aliez22.github.io)

一、Linux 中块设备驱动框架

二、块设备基本概念

1、扇区的概念来自硬件,扇区是硬件最小操作单位。

2、块的概念来自文件系统,是文件系统数据处理的最小单位。

3、段的概念来自操作系统,是内核对内存管理机制的最小单位。

4、页的概念来自操作系统,是内核内存映射管理的最小单位。

三、磁盘分区相关概念

硬盘分区的相关概念(主分区,扩展分区,逻辑分区,MBR,DBR) - 假程序猿 - 博客园 (cnblogs.com)

四、块设备驱动框架中几个重要对象 1、逻辑块设备 struct block_device { dev_t bd_dev; /* not a kdev_t - it's a search key */ int bd_openers; struct inode * bd_inode; /* will die */ struct super_block * bd_super; struct mutex bd_mutex; /* open/close mutex */ struct list_head bd_inodes; void * bd_claiming; void * bd_holder; int bd_holders; bool bd_write_holder; #ifdef CONFIG_SYSFS struct list_head bd_holder_disks; #endif struct block_device * bd_contains; unsigned bd_block_size; struct hd_struct * bd_part; /* number of times partitions within this device have been opened. */ unsigned bd_part_count; int bd_invalidated; struct gendisk * bd_disk; struct request_queue * bd_queue; struct list_head bd_list; /* * Private data. You must have bd_claim'ed the block_device * to use this. NOTE: bd_claim allows an owner to claim * the same device multiple times, the owner must take special * care to not mess up bd_private for that case. */ unsigned long bd_private; /* The counter of freeze processes */ int bd_fsfreeze_count; /* Mutex for freeze */ struct mutex bd_fsfreeze_mutex; };

1、块设备注册

/** * register_blkdev - register a new block device * * @major: the requested major device number [1..255]. If @major=0, try to * allocate any unused major number. * @name: the name of the new block device as a zero terminated string * * The @name must be unique within the system. * * The return value depends on the @major input parameter. * - if a major device number was requested in range [1..255] then the * function returns zero on success, or a negative error code * - if any unused major number was requested with @major=0 parameter * then the return value is the allocated major number in range * [1..255] or a negative error code otherwise */ int register_blkdev(unsigned int major, const char *name)

2、块设备注销

void unregister_blkdev(unsigned int major, const char *name) 2、实际块设备 struct gendisk { /* major, first_minor and minors are input parameters only, * don't use directly. Use disk_devt() and disk_max_parts(). */ int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ char disk_name[DISK_NAME_LEN]; /* name of major driver */ char *(*devnode)(struct gendisk *gd, umode_t *mode); unsigned int events; /* supported events */ unsigned int async_events; /* async events, subset of all */ /* Array of pointers to partitions indexed by partno. * Protected with matching bdev lock but stat and other * non-critical accesses use RCU. Always access through * helpers. */ struct disk_part_tbl __rcu *part_tbl; struct hd_struct part0; const struct block_device_operations *fops; struct request_queue *queue; void *private_data; int flags; struct device *driverfs_dev; // FIXME: remove struct kobject *slave_dir; struct timer_rand_state *random; atomic_t sync_io; /* RAID */ struct disk_events *ev; #ifdef CONFIG_BLK_DEV_INTEGRITY struct blk_integrity *integrity; #endif int node_id; };

1、申请 gendisk

struct gendisk *alloc_disk(int minors)

2、删除 gendisk

void del_gendisk(struct gendisk *disk)

3、将 gendisk 添加到内核

void add_disk(struct gendisk *disk)

4、设置 gendisk 容量

void set_capacity(struct gendisk *disk, sector_t size)

5、调整 gendisk 引用计数

truct kobject *get_disk(struct gendisk *disk) void put_disk(struct gendisk *disk) 3、block_device 和 gendisk 区别

struct block_device:用来描述一个块设备或者块设备的一个分区。与文件系统关系密切。

struct gendisk:描述整个块设备的特性。块设备驱动程序的主要操作对象。

对于一个包含多个分区的块设备,struct block_device 结构有多个,而 struct gendisk 结构只有一个。

4、块设备操作集 struct block_device_operations { int (*open) (struct block_device *, fmode_t); void (*release) (struct gendisk *, fmode_t); int (*rw_page)(struct block_device *, sector_t, struct page *, int rw); int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); long (*direct_access)(struct block_device *, sector_t, void **, unsigned long *pfn, long size); unsigned int (*check_events) (struct gendisk *disk, unsigned int clearing); /* ->media_changed() is DEPRECATED, use ->check_events() instead */ int (*media_changed) (struct gendisk *); void (*unlock_native_capacity) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); /* this callback is with swap_lock and sometimes page table lock held */ void (*swap_slot_free_notify) (struct block_device *, unsigned long); struct module *owner; }; 5、请求队列 struct request_queue { /* * Together with queue_head for cacheline sharing */ struct list_head queue_head; struct request *last_merge; struct elevator_queue *elevator; int nr_rqs[2]; /* # allocated [a]sync rqs */ int nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */ /* * If blkcg is not used, @q->root_rl serves all requests. If blkcg * is used, root blkg allocates from @q->root_rl and all other * blkgs from their own blkg->rl. Which one to use should be * determined using bio_request_list(). */ struct request_list root_rl; request_fn_proc *request_fn; make_request_fn *make_request_fn; prep_rq_fn *prep_rq_fn; unprep_rq_fn *unprep_rq_fn; merge_bvec_fn *merge_bvec_fn; softirq_done_fn *softirq_done_fn; rq_timed_out_fn *rq_timed_out_fn; dma_drain_needed_fn *dma_drain_needed; lld_busy_fn *lld_busy_fn; struct blk_mq_ops *mq_ops; unsigned int *mq_map; /* sw queues */ struct blk_mq_ctx __percpu *queue_ctx; unsigned int nr_queues; /* hw dispatch queues */ struct blk_mq_hw_ctx **queue_hw_ctx; unsigned int nr_hw_queues; /* * Dispatch queue sorting */ sector_t end_sector; struct request *boundary_rq; /* * Delayed queue handling */ struct delayed_work delay_work; struct backing_dev_info backing_dev_info; /* * The queue owner gets to use this for whatever they like. * ll_rw_blk doesn't touch it. */ void *queuedata; /* * various queue flags, see QUEUE_* below */ unsigned long queue_flags; /* * ida allocated id for this queue. Used to index queues from * ioctx. */ int id; /* * queue needs bounce pages for pages above this limit */ gfp_t bounce_gfp; /* * protects queue structures from reentrancy. ->__queue_lock should * _never_ be used directly, it is queue private. always use * ->queue_lock. */ spinlock_t __queue_lock; spinlock_t *queue_lock; /* * queue kobject */ struct kobject kobj; /* * mq queue kobject */ struct kobject mq_kobj; #ifdef CONFIG_PM struct device *dev; int rpm_status; unsigned int nr_pending; #endif /* * queue settings */ unsigned long nr_requests; /* Max # of requests */ unsigned int nr_congestion_on; unsigned int nr_congestion_off; unsigned int nr_batching; unsigned int dma_drain_size; void *dma_drain_buffer; unsigned int dma_pad_mask; unsigned int dma_alignment; struct blk_queue_tag *queue_tags; struct list_head tag_busy_list; unsigned int nr_sorted; unsigned int in_flight[2]; /* * Number of active block driver functions for which blk_drain_queue() * must wait. Must be incremented around functions that unlock the * queue_lock internally, e.g. scsi_request_fn(). */ unsigned int request_fn_active; unsigned int rq_timeout; struct timer_list timeout; struct list_head timeout_list; struct list_head icq_list; #ifdef CONFIG_BLK_CGROUP DECLARE_BITMAP (blkcg_pols, BLKCG_MAX_POLS); struct blkcg_gq *root_blkg; struct list_head blkg_list; #endif struct queue_limits limits; /* * sg stuff */ unsigned int sg_timeout; unsigned int sg_reserved_size; int node; #ifdef CONFIG_BLK_DEV_IO_TRACE struct blk_trace *blk_trace; #endif /* * for flush operations */ unsigned int flush_flags; unsigned int flush_not_queueable:1; struct blk_flush_queue *fq; struct list_head requeue_list; spinlock_t requeue_lock; struct work_struct requeue_work; struct mutex sysfs_lock; int bypass_depth; int mq_freeze_depth; #if defined(CONFIG_BLK_DEV_BSG) bsg_job_fn *bsg_job_fn; int bsg_job_size; struct bsg_class_device bsg_dev; #endif #ifdef CONFIG_BLK_DEV_THROTTLING /* Throttle data */ struct throtl_data *td; #endif struct rcu_head rcu_head; wait_queue_head_t mq_freeze_wq; struct percpu_ref mq_usage_counter; struct list_head all_q_node; struct blk_mq_tag_set *tag_set; struct list_head tag_set_list; };

request_queue 对象表示针对一个 gendisk 对象的所有请求的队列。

1、初始化请求队列

struct request_queue *blk_init_queue(request_fn_proc *, spinlock_t *);

2、删除请求队列

void blk_cleanup_queue(struct request_queue *);

3、制造请求函数

void blk_queue_make_request(struct request_queue *, make_request_fn *); 6、请求项 /* * Try to put the fields that are referenced together in the same cacheline. * * If you modify this structure, make sure to update blk_rq_init() and * especially blk_mq_rq_ctx_init() to take care of the added fields. */ struct request { struct list_head queuelist; union { struct call_single_data csd; unsigned long fifo_time; }; struct request_queue *q; struct blk_mq_ctx *mq_ctx; u64 cmd_flags; enum rq_cmd_type_bits cmd_type; unsigned long atomic_flags; int cpu; /* the following two fields are internal, NEVER access directly */ unsigned int __data_len; /* total data len */ sector_t __sector; /* sector cursor */ struct bio *bio; struct bio *biotail; /* * The hash is used inside the scheduler, and killed once the * request reaches the dispatch list. The ipi_list is only used * to queue the request for softirq completion, which is long * after the request has been unhashed (and even removed from * the dispatch list). */ union { struct hlist_node hash; /* merge hash */ struct list_head ipi_list; }; /* * The rb_node is only used inside the io scheduler, requests * are pruned when moved to the dispatch queue. So let the * completion_data share space with the rb_node. */ union { struct rb_node rb_node; /* sort/lookup */ void *completion_data; }; /* * Three pointers are available for the IO schedulers, if they need * more they have to dynamically allocate it. Flush requests are * never put on the IO scheduler. So let the flush fields share * space with the elevator data. */ union { struct { struct io_cq *icq; void *priv[2]; } elv; struct { unsigned int seq; struct list_head list; rq_end_io_fn *saved_end_io; } flush; }; struct gendisk *rq_disk; struct hd_struct *part; unsigned long start_time; #ifdef CONFIG_BLK_CGROUP struct request_list *rl; /* rl this rq is alloced from */ unsigned long long start_time_ns; unsigned long long io_start_time_ns; /* when passed to hardware */ #endif /* Number of scatter-gather DMA addr+len pairs after * physical address coalescing is performed. */ unsigned short nr_phys_segments; #if defined(CONFIG_BLK_DEV_INTEGRITY) unsigned short nr_integrity_segments; #endif unsigned short ioprio; void *special; /* opaque pointer available for LLD use */ int tag; int errors; /* * when request is used as a packet command carrier */ unsigned char __cmd[BLK_MAX_CDB]; unsigned char *cmd; unsigned short cmd_len; unsigned int extra_len; /* length of alignment and padding */ unsigned int sense_len; unsigned int resid_len; /* residual count */ void *sense; unsigned long deadline; struct list_head timeout_list; unsigned int timeout; int retries; /* * completion callback. */ rq_end_io_fn *end_io; void *end_io_data; /* for bidi */ struct request *next_rq; };

1、获取请求

struct request *blk_peek_request(struct request_queue *q);

2、开启请求

void blk_start_request(struct request *req)

3、获取、开启请求

struct request *blk_fetch_request(struct request_queue *q) 7、bio /* * main unit of I/O for the block layer and lower layers (ie drivers and * stacking drivers) */ struct bio { struct bio *bi_next; /* request queue link */ struct block_device *bi_bdev; unsigned long bi_flags; /* status, command, etc */ unsigned long bi_rw; /* bottom bits READ/WRITE, * top bits priority */ struct bvec_iter bi_iter; /* Number of segments in this BIO after * physical address coalescing is performed. */ unsigned int bi_phys_segments; /* * To keep track of the max segment size, we account for the * sizes of the first and last mergeable segments in this bio. */ unsigned int bi_seg_front_size; unsigned int bi_seg_back_size; atomic_t bi_remaining; bio_end_io_t *bi_end_io; void *bi_private; #ifdef CONFIG_BLK_CGROUP /* * Optional ioc and css associated with this bio. Put on bio * release. Read comment on top of bio_associate_current(). */ struct io_context *bi_ioc; struct cgroup_subsys_state *bi_css; #endif union { #if defined(CONFIG_BLK_DEV_INTEGRITY) struct bio_integrity_payload *bi_integrity; /* data integrity */ #endif }; unsigned short bi_vcnt; /* how many bio_vec's */ /* * Everything starting with bi_max_vecs will be preserved by bio_reset() */ unsigned short bi_max_vecs; /* max bvl_vecs we can hold */ atomic_t bi_cnt; /* pin count */ struct bio_vec *bi_io_vec; /* the actual vec list */ struct bio_set *bi_pool; /* * We can inline a number of vecs at the end of the bio, to avoid * double allocations for a small number of bio_vecs. This member * MUST obviously be kept at the very end of the bio. */ struct bio_vec bi_inline_vecs[0]; };

1、遍历 bio

#define __rq_for_each_bio(_bio, rq)

2、遍历 bio 中所有段

#define bio_for_each_segment(bvl, bio, iter)

3、通知 bio 处理结束

bvoid bio_endio(struct bio *bio, int error) 8、硬件信息(bvec_iter) struct bvec_iter { sector_t bi_sector; /* device address in 512 byte sectors */ unsigned int bi_size; /* residual I/O count */ unsigned int bi_idx; /* current index into bvl_vec */ unsigned int bi_bvec_done; /* number of bytes completed in current bvec */ }; 9、bio_vec /* * was unsigned short, but we might as well be ready for > 64kB I/O pages */ struct bio_vec { struct page *bv_page; unsigned int bv_len; unsigned int bv_offset; }; 10、bio逻辑架构

11、request_queue、request和bio关系

五、内存模拟硬盘驱动编写(使用内核请求队列)

参考:drivers\block\z2ram.c。

1、编写思路

1、从 RAM 中分配内存。

2、注册逻辑块设备,为应用层提供操作对象。

3、初始化请求队列。

4、添加、初始化实际块设备,为驱动提供操作对象。

5、通过注册的请求队列函数进行数据传输(可以使用内核提供,也可以自己进行构造)。

2、驱动实现 #include "linux/init.h" #include "linux/module.h" #include "linux/slab.h" #include "linux/spinlock_types.h" #include "linux/fs.h" #include "linux/genhd.h" #include "linux/hdreg.h" #include "linux/blkdev.h" #define RAMDISK_SIZE (2 * 1024 * 1024) /* 容量大小为2MB */ #define RAMDISK_NAME "ramdisk" /* 名字 */ #define RADMISK_MINOR 3 /* 表示有三个磁盘分区!不是次设备号为3! */ typedef struct{ unsigned char *ramdiskbuf; /* ramdisk内存空间,用于模拟块设备 */ spinlock_t lock; /* 自旋锁 */ int major; /* 主设备号 */ struct request_queue *queue;/* 请求队列 */ struct gendisk *gendisk; /* gendisk */ }newchrdev_t; newchrdev_t newchrdev; /* * @description : 打开块设备 * @param - dev : 块设备 * @param - mode : 打开模式 * @return : 0 成功;其他 失败 */ int ramdisk_open(struct block_device *dev, fmode_t mode) { printk("ramdisk open\r\n"); return 0; } /* * @description : 释放块设备 * @param - disk : gendisk * @param - mode : 模式 * @return : 0 成功;其他 失败 */ void ramdisk_release(struct gendisk *disk, fmode_t mode) { printk("ramdisk release\r\n"); } /* * @description : 获取磁盘信息 * @param - dev : 块设备 * @param - geo : 模式 * @return : 0 成功;其他 失败 */ int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo) { /* 这是相对于机械硬盘的概念 */ geo->heads = 2; /* 磁头 */ geo->cylinders = 32; /* 柱面 */ geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */ return 0; } /* * 块设备操作函数 */ static struct block_device_operations ramdisk_fops = { .owner = THIS_MODULE, .open = ramdisk_open, .release = ramdisk_release, .getgeo = ramdisk_getgeo, }; /* * @description : 处理传输过程 * @param-req : 请求 * @return : 无 */ static void ramdisk_transfer(struct request *req) { unsigned long start = blk_rq_pos(req) /* 针对请求做具体的传输处理 */ ramdisk_transfer(req); /* 判断是否为最后一个请求,如果不是的话就获取下一个请求 * 循环处理完请求队列中的所有请求。 */ if (!__blk_end_request_cur(req, err)) req = blk_fetch_request(q); } } /* * @description : 驱动出口函数 * @param : 无 * @return : 无 */ static int __init ramdisk_init(void) { int ret = 0; printk("ramdisk init\r\n"); /* 1、申请用于ramdisk内存 */ newchrdev.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL); if(newchrdev.ramdiskbuf == NULL) { ret = -EINVAL; goto ram_fail; } /* 2、初始化自旋锁 */ spin_lock_init(&newchrdev.lock); /* 3、注册块设备(逻辑块设备:为应用层提供一个操作对象) */ newchrdev.major = register_blkdev(0, RAMDISK_NAME); /* 由系统自动分配主设备号 */ if(newchrdev.major ret = -EINVAL; goto gendisk_alloc_fail; } /* 5、分配并初始化请求队列 */ newchrdev.queue = blk_init_queue(ramdisk_request_fn, &newchrdev.lock); if(!newchrdev.queue) { ret = EINVAL; goto blk_init_fail; } /* 6、添加(注册)disk * (1)、关联逻辑块设备和物理块设备 * (2)、为物理块设备添加操作集和请求队列 * (3)、为物理块设备设置属性 */ newchrdev.gendisk->major = newchrdev.major; /* 主设备号 */ newchrdev.gendisk->first_minor = 0; /* 第一个次设备号(起始次设备号) */ newchrdev.gendisk->fops = &ramdisk_fops; /* 操作函数 */ newchrdev.gendisk->private_data = &newchrdev; /* 私有数据 */ newchrdev.gendisk->queue = newchrdev.queue; /* 请求队列 */ sprintf(newchrdev.gendisk->disk_name, RAMDISK_NAME); /* 名字 */ set_capacity(newchrdev.gendisk, RAMDISK_SIZE/512); /* 设备容量(单位为扇区) */ add_disk(newchrdev.gendisk); return 0; blk_init_fail: put_disk(newchrdev.gendisk); //del_gendisk(ramdisk.gendisk); gendisk_alloc_fail: unregister_blkdev(newchrdev.major, RAMDISK_NAME); register_blkdev_fail: kfree(newchrdev.ramdiskbuf); /* 释放内存 */ ram_fail: return ret; } /* * @description : 驱动出口函数 * @param : 无 * @return : 无 */ static void __exit ramdisk_exit(void) { printk("ramdisk exit\r\n"); /* 释放gendisk */ del_gendisk(newchrdev.gendisk); put_disk(newchrdev.gendisk); /* 清除请求队列 */ blk_cleanup_queue(newchrdev.queue); /* 注销块设备 */ unregister_blkdev(newchrdev.major, RAMDISK_NAME); /* 释放内存 */ kfree(newchrdev.ramdiskbuf); } module_init(ramdisk_init); module_exit(ramdisk_exit); MODULE_LICENSE("GPL"); 3、测试 # ls ramdisk.ko # # insmod ramdisk.ko ramdisk init ramdisk major = 254 ramdisk open ramdisk release # # ls -l /dev/ramdisk brw-rw---- 1 root root 254, 0 Jan 1 00:13 /dev/ramdisk # # rmmod ramdisk.ko ramdisk exit # # ls -l /dev/ramdisk ls: /dev/ramdisk: No such file or directory # # insmod ramdisk.ko ramdisk init ramdisk major = 254 ramdisk open ramdisk release # # rmmod ramdisk.ko ramdisk exit # # insmod ramdisk.ko ramdisk init ramdisk major = 254 ramdisk open ramdisk release # ls -l /dev/ramdisk brw-rw---- 1 root root 254, 0 Jan 1 00:14 /dev/ramdisk # # fdisk -l Disk /dev/mmcblk0: 15 GB, 15931539456 bytes, 31116288 sectors 1936 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Device ramdisk open Boot StartCHS EndCHS Stramdisk release artLBA EndLBA Sectors Sizramdisk open e Id Type /dev/mmcblk0p1 0,13ramdisk release 0,3 1023,254,63 8192 31116287 31108096 14.8G c Win95 FAT32 (LBA) Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors 238592 cylinders, 4 heads, 16 sectors/track Units: sectors of 1 * 512 = 512 bytes Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type /dev/mmcblk1p1 * 0,32,33 4,52,48 2048 67583 65536 32.0M c Win95 FAT32 (LBA) /dev/mmcblk1p2 4,52,49 950,129,11 67584 15269887 15202304 7423M 83 Linux Disk /dev/mmcblk1boot1: 4 MB, 4194304 bytes, 8192 sectors 128 cylinders, 4 heads, 16 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/mmcblk1boot1 doesn't contain a valid partition table Disk /dev/mmcblk1boot0: 4 MB, 4194304 bytes, 8192 sectors 128 cylinders, 4 heads, 16 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/mmcblk1boot0 doesn't contain a valid partition table Disk /dev/ramdisk: 2 MB, 2097152 bytes, 4096 sectors 32 cylinders, 2 heads, 64 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/ramdisk doesn't contain a valid partition table # 六、内存模拟硬盘驱动编写(自定义请求队列)

参考:drivers\block\zram\zram_drv.c。

1、编写思路

1、从 RAM 中分配内存。

2、注册逻辑块设备,为应用层提供操作对象。

3、设置“制造请求”函数。

4、添加、初始化实际块设备,为驱动提供操作对象。

5、通过注册的请求队列函数进行数据传输(可以使用内核提供,也可以自己进行构造)。

2、驱动实现 #include "linux/init.h" #include "linux/module.h" #include "linux/slab.h" #include "linux/spinlock_types.h" #include "linux/fs.h" #include "linux/genhd.h" #include "linux/hdreg.h" #include "linux/blkdev.h" #define RAMDISK_SIZE (2 * 1024 * 1024) /* 容量大小为2MB */ #define RAMDISK_NAME "ramdisk" /* 名字 */ #define RADMISK_MINOR 3 /* 表示有三个磁盘分区!不是次设备号为3! */ typedef struct{ unsigned char *ramdiskbuf; /* ramdisk内存空间,用于模拟块设备 */ spinlock_t lock; /* 自旋锁 */ int major; /* 主设备号 */ struct request_queue *queue;/* 请求队列 */ struct gendisk *gendisk; /* gendisk */ }newchrdev_t; newchrdev_t newchrdev; /* * @description : 打开块设备 * @param - dev : 块设备 * @param - mode : 打开模式 * @return : 0 成功;其他 失败 */ int ramdisk_open(struct block_device *dev, fmode_t mode) { printk("ramdisk open\r\n"); return 0; } /* * @description : 释放块设备 * @param - disk : gendisk * @param - mode : 模式 * @return : 0 成功;其他 失败 */ void ramdisk_release(struct gendisk *disk, fmode_t mode) { printk("ramdisk release\r\n"); } /* * @description : 获取磁盘信息 * @param - dev : 块设备 * @param - geo : 模式 * @return : 0 成功;其他 失败 */ int ramdisk_getgeo(struct block_device *dev, struct hd_geometry *geo) { /* 这是相对于机械硬盘的概念 */ geo->heads = 2; /* 磁头 */ geo->cylinders = 32; /* 柱面 */ geo->sectors = RAMDISK_SIZE / (2 * 32 *512); /* 一个磁道上的扇区数量 */ return 0; } /* * 块设备操作函数 */ static struct block_device_operations ramdisk_fops = { .owner = THIS_MODULE, .open = ramdisk_open, .release = ramdisk_release, .getgeo = ramdisk_getgeo, }; #if 0 /* * @description : 处理传输过程 * @param-req : 请求 * @return : 无 */ static void ramdisk_transfer(struct request *req) { unsigned long start = blk_rq_pos(req) /* 针对请求做具体的传输处理 */ ramdisk_transfer(req); /* 判断是否为最后一个请求,如果不是的话就获取下一个请求 * 循环处理完请求队列中的所有请求。 */ if (!__blk_end_request_cur(req, err)) req = blk_fetch_request(q); } } #endif /* * @description : “制造请求”函数 * @param-q : 请求队列 * @return : 无 */ void ramdisk_make_request_fn(struct request_queue *q, struct bio *bio) { int offset; struct bio_vec bvec; struct bvec_iter iter; unsigned long len = 0; offset = (bio->bi_iter.bi_sector) int ret = 0; printk("ramdisk init\r\n"); /* 1、申请用于ramdisk内存 */ newchrdev.ramdiskbuf = kzalloc(RAMDISK_SIZE, GFP_KERNEL); if(newchrdev.ramdiskbuf == NULL) { ret = -EINVAL; goto ram_fail; } /* 2、初始化自旋锁 */ spin_lock_init(&newchrdev.lock); /* 3、注册块设备(逻辑块设备:为应用层提供一个操作对象) */ newchrdev.major = register_blkdev(0, RAMDISK_NAME); /* 由系统自动分配主设备号 */ if(newchrdev.major ret = -EINVAL; goto gendisk_alloc_fail; } /* 5、分配并初始化请求队列 */ #if 0 newchrdev.queue = blk_init_queue(ramdisk_request_fn, &newchrdev.lock); if(!newchrdev.queue) { ret = EINVAL; goto blk_init_fail; } #endif newchrdev.queue = blk_alloc_queue(GFP_KERNEL); if(!newchrdev.queue){ ret = -EINVAL; goto blk_allo_fail; } /* 6、设置“制造请求”函数 */ blk_queue_make_request(newchrdev.queue, ramdisk_make_request_fn); /* 7、添加(注册)disk * (1)、关联逻辑块设备和物理块设备 * (2)、为物理块设备添加操作集和请求队列 * (3)、为物理块设备设置属性 */ newchrdev.gendisk->major = newchrdev.major; /* 主设备号 */ newchrdev.gendisk->first_minor = 0; /* 第一个次设备号(起始次设备号) */ newchrdev.gendisk->fops = &ramdisk_fops; /* 操作函数 */ newchrdev.gendisk->private_data = &newchrdev; /* 私有数据 */ newchrdev.gendisk->queue = newchrdev.queue; /* 请求队列 */ sprintf(newchrdev.gendisk->disk_name, RAMDISK_NAME); /* 名字 */ set_capacity(newchrdev.gendisk, RAMDISK_SIZE/512); /* 设备容量(单位为扇区) */ add_disk(newchrdev.gendisk); return 0; blk_allo_fail: put_disk(newchrdev.gendisk); //del_gendisk(ramdisk.gendisk); gendisk_alloc_fail: unregister_blkdev(newchrdev.major, RAMDISK_NAME); register_blkdev_fail: kfree(newchrdev.ramdiskbuf); /* 释放内存 */ ram_fail: return ret; } /* * @description : 驱动出口函数 * @param : 无 * @return : 无 */ static void __exit ramdisk_exit(void) { printk("ramdisk exit\r\n"); /* 释放gendisk */ del_gendisk(newchrdev.gendisk); put_disk(newchrdev.gendisk); /* 清除请求队列 */ blk_cleanup_queue(newchrdev.queue); /* 注销块设备 */ unregister_blkdev(newchrdev.major, RAMDISK_NAME); /* 释放内存 */ kfree(newchrdev.ramdiskbuf); } module_init(ramdisk_init); module_exit(ramdisk_exit); MODULE_LICENSE("GPL"); 3、测试 # ls ramdisk.ko # insmod ramdisk.ko ramdisk init ramdisk major = 254 ramdisk open ramdisk release # ls -l /dev/ramdisk brw-rw---- 1 root root 254, 0 Jan 1 00:51 /dev/ramdisk # # rmmod ramdisk.ko ramdisk exit # # ls -l /dev/ramdisk ls: /dev/ramdisk: No such file or directory # # insmod ramdisk.ko ramdisk init ramdisk major = 254 ramdisk open ramdisk release # # ls -l /dev/ramdisk brw-rw---- 1 root root 254, 0 Jan 1 00:51 /dev/ramdisk # # rmmod ramdisk.ko ramdisk exit # # random: nonblocking pool is initialized # ls -l /dev/ramdisk ls: /dev/ramdisk: No such file or directory # # # # insmod ramdisk.ko ramdisk init ramdisk major = 254 ramdisk open ramdisk release # # fdisk -l Disk /dev/mmcblk0: 15 GB, 15931539456 bytes, 31116288 sectors 1936 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Device Boot StartCHS EndCHS Sramdisk open tartLBA EndLBA Sectors Siramdisk release ze Id Type /dev/mmcblk0p1 0,1ramdisk open 30,3 1023,254,63 8192 31116287 31108096 14.8G c Wiramdisk release n95 FAT32 (LBA) Disk /dev/mmcblk1: 7456 MB, 7818182656 bytes, 15269888 sectors 238592 cylinders, 4 heads, 16 sectors/track Units: sectors of 1 * 512 = 512 bytes Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type /dev/mmcblk1p1 * 0,32,33 4,52,48 2048 67583 65536 32.0M c Win95 FAT32 (LBA) /dev/mmcblk1p2 4,52,49 950,129,11 67584 15269887 15202304 7423M 83 Linux Disk /dev/mmcblk1boot1: 4 MB, 4194304 bytes, 8192 sectors 128 cylinders, 4 heads, 16 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/mmcblk1boot1 doesn't contain a valid partition table Disk /dev/mmcblk1boot0: 4 MB, 4194304 bytes, 8192 sectors 128 cylinders, 4 heads, 16 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/mmcblk1boot0 doesn't contain a valid partition table Disk /dev/ramdisk: 2 MB, 2097152 bytes, 4096 sectors 32 cylinders, 2 heads, 64 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/ramdisk doesn't contain a valid partition table #


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3